package lol.clann.minecraft.springboot;

import lol.clann.minecraft.springboot.remap.ClassLoaderContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableAsync;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.CodeSource;
import java.security.ProtectionDomain;

/**
 *
 * @author pyz
 * @date 2019/4/8 7:46 PM
 */
@Slf4j
@EnableAsync
@SpringBootApplication
@ComponentScan({"lol.clann", "com.ruoshui"})
@EnableAutoConfiguration
@ImportAutoConfiguration(exclude = {
//        自己动手丰衣足食
        DataSourceAutoConfiguration.class,
//        有端有冲突
        GsonAutoConfiguration.class,
//        u端好像不允许注册jmx
        JmxAutoConfiguration.class,
//        不自动配置jackson,因为改了jackson包路径
        JacksonAutoConfiguration.class
})
public class Main {

    private static final Method addURL;
    private static final String osName = System.getProperty("os.name").toLowerCase();
    private static final URLClassLoader classLoader = (URLClassLoader) Main.class.getClassLoader();
    private static SpringApplication application;
    private static ConfigurableApplicationContext applicationContext;

    static {
        try {
            addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
            addURL.setAccessible(true);
        } catch (Throwable var1) {
            throw new RuntimeException(var1);
        }
    }

    public static void main(String[] args) throws Exception {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(Main.class.getClassLoader());
        ClassLoaderContext.put(Main.class.getClassLoader());
        try {
            File pluginDir = getJar(Main.class.getClassLoader().getClass()).toFile().getParentFile();
            addPluginsClassPath(pluginDir);
            LOGGER.info("starting springboot");
            application = new SpringApplication(Main.class);
            application.setBannerMode(Banner.Mode.OFF);
            applicationContext = application.run(args);
        } finally {
            ClassLoaderContext.pop();
            Thread.currentThread().setContextClassLoader(classLoader);
        }
    }

    public static void stop() {
        LOGGER.info("stopping spring context");
        applicationContext.close();
        application = null;
        applicationContext = null;
        LOGGER.info("stopped spring context");
    }

    private static void addURL(URLClassLoader loader, URL url) throws InvocationTargetException, IllegalAccessException {
        addURL.invoke(loader, url);
    }

    private static Path getJar(Class clazz) throws Exception {
        ProtectionDomain protectionDomain = clazz.getProtectionDomain();
        CodeSource codeSource = protectionDomain.getCodeSource();
        URI location = codeSource != null ? codeSource.getLocation().toURI() : null;
        String path = location != null ? location.getSchemeSpecificPart() : null;
        if (path == null) {
            throw new IllegalStateException("Unable to determine code source archive");
        } else {
            if (osName.contains("windows") && path.startsWith("/")) {
                path = path.substring(1);
            }
            return Paths.get(path);
        }
    }

    private static void addPluginsClassPath(File pluginDir) throws MalformedURLException, InvocationTargetException, IllegalAccessException {
        File libDir = pluginDir.toPath().resolve("SpringBootPlugin").resolve("plugins").toFile();
        if (!libDir.exists()) {
            libDir.mkdirs();
        }
        for (File file : libDir.listFiles()) {
            if (file.getName().endsWith(".jar")) {
                addURL(classLoader, file.toURI().toURL());
                LOGGER.info("find springboot plugin file:" + file.getAbsolutePath());
            }
        }
    }

}
